﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace myLittleC
{
    class Parser
    {
        protected ISyntaxNode obj = null;

        protected bool MayDeclaration {get; set;}

        protected Tokenizer mTokenizer;

        public Parser(String file)
        {
            MayDeclaration = false;
            mTokenizer = new Tokenizer(file);
        }

        public void parse()
        {
           
          
             if (mTokenizer.TokenType != TokenType.EOF)
                obj = parseStatement();
             if (mTokenizer.TokenType != TokenType.EOF)
                throw new ComExeption(ERRORS.BadExpression, mTokenizer.Position);
        }
       
        public  override String ToString() { return obj.ToString("", true); }             

        protected virtual bool isDeclareVareable(String Name) { return true; } 

        protected virtual ISyntaxNode parseStatement() { return parseExpr(); }

        protected ISyntaxExpression parseExpr() { return parseBinaryExpr(0); }

        private ISyntaxExpression parseBinaryExpr(int prioritet)
        {
            ISyntaxExpression left = (prioritet + 1 < 10) ? parseBinaryExpr(prioritet + 1) : ParseUnaryExpr();

            if (prioritet == 0 && ChekPrioritet(prioritet, mTokenizer.SubType) && !IsLValue(ref left))
                 throw new ComExeption(ERRORS.BadLValue, mTokenizer.Position);
            
            while (ChekPrioritet(prioritet, mTokenizer.SubType))
            {
                SubTokenType stt = mTokenizer.SubType;
                mTokenizer.getNextToken();
                ISyntaxExpression right = parseBinaryExpr(prioritet + (prioritet == 0 ? 0 :1));
                
                if (right == null)
                    throw new ComExeption(((mTokenizer.SubType == SubTokenType.EOF) ? ERRORS.EOF : ERRORS.BadExpression), mTokenizer.Position);
                left = new SyntaxBinaryOp(stt, left, right);
            }
            return left;
        }

        ISyntaxExpression ParseUnaryExpr()
        {
            ISyntaxExpression parsedExpresion;

            if (mTokenizer.isStartParanthesis())
            {
                mTokenizer.getNextToken();
                parsedExpresion = parseExpr();

                if (!mTokenizer.isFinishParanthesis())
                    throw new ComExeption(ERRORS.UnExpRightParan, mTokenizer.Position);
               
                mTokenizer.getNextToken();
                return parsedExpresion;
            }

            else if (mTokenizer.isIdenteficator())
            {
               if(!isDeclareVareable(mTokenizer.ActiveToken.Value.ToString())) 
                    throw new ComExeption(ERRORS.DublDeclaration, mTokenizer.ActiveToken.Position);
            
                parsedExpresion = new SyntaxIdentefier(mTokenizer.ActiveToken.Value as String);
                mTokenizer.getNextToken();

                if (mTokenizer.SubType == SubTokenType.Increment || mTokenizer.SubType == SubTokenType.Decrement)
                {
                    parsedExpresion = new SynataxUnaryOp(mTokenizer.SubType, parsedExpresion, true);
                    mTokenizer.getNextToken();
                }
                else if (mTokenizer.isIdenteficator() || mTokenizer.isConst() || mTokenizer.isStartParanthesis())
                    throw new ComExeption(ERRORS.BadExpression, mTokenizer.Position);
            }
                
            else if (mTokenizer.isConst())
            {parsedExpresion = null;
                if (mTokenizer.isConstInt())
                    parsedExpresion = new SyntaxConstInt(mTokenizer.ActiveToken);
                else if  (mTokenizer.isConstReal())
                    parsedExpresion = new SyntaxConstReal(mTokenizer.ActiveToken);
                    
                mTokenizer.getNextToken(); 
                
                if(mTokenizer.isNoType())
                    throw new ComExeption(ERRORS.BadUnaryExp, mTokenizer.Position);
                else if (mTokenizer.isIdenteficator() || mTokenizer.isConst() || mTokenizer.isStartParanthesis())
                    throw new ComExeption(ERRORS.BadExpression, mTokenizer.Position);
            }
            else if (mTokenizer.isUnaryOp())
            {
                SubTokenType stt = mTokenizer.SubType;
                mTokenizer.getNextToken();
                parsedExpresion = ParseUnaryExpr();
                if (parsedExpresion == null)
                    throw new ComExeption((mTokenizer.TokenType == TokenType.EOF ? ERRORS.EOF : ERRORS.BadUnaryExp), mTokenizer.Position);

                parsedExpresion = new SynataxUnaryOp(stt, parsedExpresion);
            }
            else throw new ComExeption(ERRORS.BadExpression, mTokenizer.Position);

            return parsedExpresion;
        }

        private bool ChekPrioritet(int Prioritet, SubTokenType SubType)
        {
            if (SubTokenType.Increment <= SubType && SubType <= SubTokenType.Not)
                return Prioritet == 10;
            else if (SubTokenType.Multiply <= SubType && SubType <= SubTokenType.Div)
                return Prioritet == 9;
            else if (SubTokenType.Add == SubType || SubType == SubTokenType.Sub)
                return Prioritet == 8;
            else if (SubTokenType.SHL == SubType || SubType == SubTokenType.SHR)
                return Prioritet == 7;
            else if (SubTokenType.Low <= SubType && SubType <= SubTokenType.HiOrEq)
                return Prioritet == 6;
            else if (SubTokenType.Equal == SubType || SubType == SubTokenType.NotEqual)
                return Prioritet == 5;
            else if (SubTokenType.And == SubType)
                return Prioritet == 4;
            else if (SubTokenType.Or == SubType)
                return Prioritet == 3;
            else if (SubTokenType.LogicalAnd == SubType)
                return Prioritet == 2;
            else if (SubTokenType.LogicalOr == SubType)
                return Prioritet == 1;
            else if (SubTokenType.Assignment <= SubType && SubType <= SubTokenType.DivAssigment)
                return Prioritet == 0;

            return false;
        }

        private bool IsLValue(ref ISyntaxExpression left)
        {
            return (left is SyntaxIdentefier || (left is SynataxUnaryOp && (left as SynataxUnaryOp).Operation == SubTokenType.Multiply));
        }
    }

    class ParseWithSimpleeDef : Parser
    {
        SimpleCharsTable table = new SimpleCharsTable();

        public ParseWithSimpleeDef(String file) :base(file) { MayDeclaration = true;}
        public new String ToString() { return table.ToString() + "\n" + ( obj == null ? " " : obj.ToString("", true) ); }

        protected bool isDeclaration { get { return true; } }

        protected override ISyntaxNode parseStatement()
        {
            while (MayDeclaration)            
            {
                String na = mTokenizer.ActiveToken.Name;
                mTokenizer.getNextToken();

                if (mTokenizer.ActiveToken.SubType != SubTokenType.Identeficator)
                    throw new ComExeption(ERRORS.BadDeclaration, mTokenizer.ActiveToken.Position);
                
                switch (na)
                {
                    case "var":
                        table.Add(mTokenizer.ActiveToken.Name, SimpleCharType.Variable);
                        break;
                    case "Struct": 
                        table.Add(mTokenizer.ActiveToken.Name, SimpleCharType.Struct);
                        break;
                    case "array": 
                        table.Add(mTokenizer.ActiveToken.Name, SimpleCharType.Array);
                        break;
                    case "procedure": 
                        table.Add(mTokenizer.ActiveToken.Name, SimpleCharType.Procedure);
                        break;
                    default:
                        throw new ComExeption(ERRORS.NotDefType, mTokenizer.Position);
                }
                mTokenizer.getNextToken();
                IsMayDeclaration();
            }
            return base.parseStatement();
        }

        private bool IsMayDeclaration()
        {
            String na = mTokenizer.ActiveToken.Name;
            MayDeclaration = (na == "var" || na == "Struct" || na == "array" || na == "procedure");
            return MayDeclaration;
        }

        protected  ExpType typeBinaryExpression(SubTokenType Operation, ExpType OperandA, ExpType OperandB)
        {   return ExpType.Float;}

        protected override bool isDeclareVareable(String Name) { return table.find(Name) == -1 ? false : true; }
        
    }

    class ParserWithStatement : Parser
    {
        SyntaxBlok Global = new SyntaxBlok();
     

        public ParserWithStatement(String file) : base(file) {}        

        private bool UnicIdenteficator(SyntaxBlok iBlok) {   return UnicIdenteficator(mTokenizer.Name, iBlok);}
		private bool UnicIdenteficator(String Identificator,SyntaxBlok iBlok) {   return mTokenizer.isIdenteficator() && iBlok.Find(Identificator) == null; }
        private bool isType(SyntaxBlok iBlok, String TypeName) 
		{
			return mTokenizer.ResTypes() || iBlok.FindTypeWithParent(TypeName) != null; 
		}
		
		private bool MayDeclareFunk(SyntaxBlok iBlok, String FunkName) 
		{	return iBlok == Global;}
		
        private bool MayDefinitionFunk(SyntaxBlok iBlok, String FunkName)
        {    
			if (MayDeclareFunk(iBlok, FunkName))
			{
				SymFunk  funk = iBlok.Table.FindFunk(FunkName);
				if (funk != null && funk.isPrototipe )
					return true;
			}
			return false;
		}      
		
		

        private SymType CreateType(SyntaxBlok iBlok)
        {
            return   mTokenizer.isInt()
                        ? new SymTypeInt()
                          : (mTokenizer.isFloat()
                            ? new SymTypeFloat()
                              : (SymType)iBlok.FindTypeWithParent(mTokenizer.Name));
        }

        protected override ISyntaxNode parseStatement() {	return parseBlok(Global);}
		
        protected ISyntaxNode parseBlok(SyntaxBlok iBlok)
        {
            while (!mTokenizer.isFinishRace() && !mTokenizer.isEnd())
            {
                if (isType(iBlok, mTokenizer.Name))
                {
                    SymType Type = CreateType(iBlok);
                    mTokenizer++;
                    
                    ParseDefinition(iBlok, Type);
                }
                else if (mTokenizer.isStruct())
                {
                    mTokenizer++;
                    SymType Type = ParseDefinitionStruct(iBlok);
               
                    if (mTokenizer.isIdenteficator())
                        ParseDefinition(iBlok, Type);
                    else if (mTokenizer.isSemicolon())
                        mTokenizer++;
                    else
                        throw new ComExeption(ERRORS.UnIdefSymbol, mTokenizer.Position);
                }
				else if (mTokenizer.isTypedef())
				{
					mTokenizer++;
					iBlok.Add(ParseDefinitionTypedef(iBlok));
					mTokenizer++;
				}
                else
                {
                    iBlok.EndDefentions(); //тк не обьявление то обьявлять в этом блоке уже нелья
                    if (mTokenizer.isStartRace())
                    {
                        //идея такая, у нас кароче есть блок глобал, все осталььные его наследники,
                        //когда встречаем определение нового блока, мы начинаем парсинг для него

                        mTokenizer++;
                        iBlok.Add(parseBlok(new SyntaxBlok()));
                    }
                    else
                        iBlok.Add(parseExpr()); //последний вариант ето выражение
                }
            }

             if (mTokenizer.isEnd() && iBlok != Global)
                throw new ComExeption(ERRORS.UnExpFinRace);
            mTokenizer.getNextToken();
            return iBlok;
        }

       
        private void ParseDefinition(SyntaxBlok iBlok, SymType iType)
        {
            //ща должен быть идентификатор и уникальный, иначе ексепшен
            if (!(mTokenizer).isIdenteficator())
                throw new ComExeption(ERRORS.UnExpIdent, mTokenizer.Position);
            
            String Name = mTokenizer.Name;
            if (UnicIdenteficator(iBlok))
            {
                if (!iBlok.MayDefs)
                    throw new ComExeption(ERRORS.CantDefsVar, mTokenizer.Position);

                if (mTokenizer++.isStartParanthesis()) //если функция то просто добавляем, инече будет цикл
                {
                    if (!MayDeclareFunk(iBlok, Name))
                        throw new ComExeption(ERRORS.CantDefsFunk, mTokenizer.Position);
					SymFunk t = ParseDeclareFunk(Name, iType);
                   iBlok.Add(t);
                }
                else 
                {
                   
                    iBlok.Add( DefineVar(Name, iType));
                    while (mTokenizer.isComma())
                    {
                        mTokenizer++;
                        if (UnicIdenteficator(iBlok))
                        {
                            Name = mTokenizer.Name;
                            mTokenizer++;
                            iBlok.Add(DefineVar(Name, iType));
                        }
                        else
                            throw new ComExeption(ERRORS.NotUnicDef, mTokenizer.Position);
                    }
                   
                    if (!mTokenizer.isSemicolon())
                        throw new ComExeption(ERRORS.UnExpSemicolon, mTokenizer.Position);
                    mTokenizer++;
                }
            }
            else if ((mTokenizer++).isStartParanthesis())
			{
				if (MayDefinitionFunk(iBlok, Name ))
	            {
					SymFunk funk = iBlok.Table.FindFunk(Name);
	                for (; !mTokenizer.isStartRace(); mTokenizer++) ;
	                funk.Body = ParseDefinitionFunk(funk.Args);
	            }
				
				else
                	throw new ComExeption(
					                      iBlok.Table.FindFunk(Name) == null ?
					                      	ERRORS.NotUnicDef : ERRORS.CantDefsFunk, 
					                      mTokenizer.Position);
			}
			else
                	throw new ComExeption(ERRORS.NotUnicDef, mTokenizer.Position);
            
        }

        private ISymbol DefineVar(String iName, SymType iType)
        {
            ISymbol newVar;
            if (mTokenizer.isStartSquareBracket())
                newVar = ParseDefinitionArrays(iName, iType);
            else 
            {
                newVar = new SymVar(iName, iType);
                if (mTokenizer.isAssignment())
                {
                    mTokenizer++;
                    (newVar as SymVar).Value = parseExpr();
                }
            }
           
            return newVar;
        }


        private SymFunk ParseDeclareFunk(String Name, SymType Type)
        {
            SymbolTable Args = new SymbolTable();
            
            if (!(++mTokenizer).isFinishParanthesis())
            {
                do
                {
                    if (mTokenizer.isComma()) mTokenizer++;
                    if (!isType(Global, mTokenizer.Name))
                        throw new ComExeption(ERRORS.UnExpType, mTokenizer.Position);
                    
					SymType type = CreateType(Global);                   
                    Args.Add(new SymVar((mTokenizer++).Name, type));
                    mTokenizer ++;
                }
                while (!mTokenizer.isFinishParanthesis() && (mTokenizer).isComma());
            }
           
            SymFunk nf =  new SymFunk(Name, Type, Args);
            if (mTokenizer++.SubType == SubTokenType.SPTT_LBRACE)
                nf.Body = ParseDefinitionFunk(Args);
            else
            {
                if (mTokenizer.isSemicolon()) mTokenizer++;
                else throw new ComExeption(ERRORS.UnExpSemicolon);
            }
            return nf;
        }

        private SyntaxBlok ParseDefinitionFunk(SymbolTable Args)
        {
            SyntaxBlok Body = new SyntaxBlok(Global);
            Body.Table.Copy(Args);
            mTokenizer.getNextToken();
            parseBlok(Body);
            return Body;
        }

        private SymVarArray ParseDefinitionArrays(String iName, SymType iType)
        {
            mTokenizer.getNextToken();
            if (!(mTokenizer.ActiveToken.Value is Int32))
                throw new ComExeption(ERRORS.NotArraySize, mTokenizer.Position);
            
            int i = (Int32)mTokenizer.ActiveToken.Value;
            
            if (!mTokenizer++.isFinishSquareBracket())
                throw new ComExeption(ERRORS.UnExpRBreak, mTokenizer.Position);

            mTokenizer++;
            return new SymVarArray(iName, iType, i);
        }

        private SymStruct ParseDefinitionStruct(SyntaxBlok iBlok)
        {

            SymStruct newStuct ;

            if (mTokenizer.isIdenteficator())
			//	throw new ComExeption(ERRORS.UnExpIdent, mTokenizer.Position);
			{
	            if (UnicIdenteficator(mTokenizer.Name, iBlok)) 
				{	
					newStuct = new SymStruct(mTokenizer.Name); 
					iBlok.Add(newStuct);
				}
				else
				{
					ISymbol sym = iBlok.Table.FindType(mTokenizer.Name);
	                if ( !( sym is SymStruct) || !(sym as SymStruct).isPrototipe)                
						throw new ComExeption(ERRORS.NotUnicDef, mTokenizer.Position);
					newStuct = (sym as SymStruct);  
				}	
				mTokenizer++;
			}
			else 
			{	
				newStuct = new SymStruct(); 
				iBlok.Add(newStuct);
			}
			
			
            if (mTokenizer.isStartRace())
            {
                mTokenizer++;
                SyntaxBlok b = new SyntaxBlok(iBlok);
                parseBlok(b);
				
                if (b.HaveNodes) throw new ComExeption(ERRORS.CanDefsExpr, mTokenizer.Position);
				if (!b.Table.HaveVars) throw new ComExeption(ERRORS.EmpryStruct, mTokenizer.Position);
                
				newStuct.Items = b.Table;
            }
            return newStuct;
        }

        private SymTypedef ParseDefinitionTypedef(SyntaxBlok iBlok)
        {
			SymType Type  ;
			if (isType(iBlok, mTokenizer.Name))
			{
				Type  = CreateType(iBlok);
				mTokenizer++;
			}
			else 
			{
			   	mTokenizer++;
				Type  = ParseDefinitionStruct(iBlok);	
			}
            
            
            if (!(mTokenizer).isIdenteficator()) throw new ComExeption(ERRORS.UnExpIdent, mTokenizer.Position);
			String name = mTokenizer.Name;
			
			if(!(++mTokenizer).isSemicolon()) throw new ComExeption(ERRORS.UnExpSemicolon);
			
			return new SymTypedef(name, Type);
        }

        public String WriteTable() { return Global.WriteTable("", "Global"); }
        

       

        #region Проверка валидности кода
        /* 
       * 
       * class ExpPair
        {   
            ExpType type;
            ISyntaxExpression Expression;
        }

       * protected virtual bool BynaryExpChech(SyntaxBinaryOp Expression);

        protected virtual bool BynaryExpChech(SyntaxBinaryOp Expression)
        {
            ExpType typeLeft = Expression.Left;
            ExpType typeRight = Expression.Left;            
        }

        protected virtual bool ExpChech(ISyntaxExpression Expression)
        {
            if (Expression is SynataxUnaryOp)
            {

            }
            else if (Expression is SyntaxBinaryOp)
            {
                
            }
            return true;
        }

        protected virtual ExpType checkExpression(ISyntaxExpression Expression)
        {
            return (Expression is SynataxUnaryOp) ?
                typeUnaryExpression(Expression.Operation,
                    (Expression.Arguments[0] is ISyntaxExpression) ?
                        checkExpression(Expression.Arguments[0] as SynataxUnaryOp) : (Expression.Arguments[0] as IVarOrConst).ExType
                    )

                :
                 typeBinaryExpression(Expression.Operation, 
                    (Expression.Arguments[0] is ISyntaxExpression) ? 
                        checkExpression(Expression.Arguments[0] as SyntaxBinaryOp) : (Expression.Arguments[0] as IVarOrConst).ExType,
                    (Expression.Arguments[0]is ISyntaxExpression) ? 
                        checkExpression(Expression.Arguments[1] as SyntaxBinaryOp) : (Expression.Arguments[1] as IVarOrConst).ExType
                 );
        }

        protected virtual ExpType typeUnaryExpression(SubTokenType Operation, ExpType Operand) { return ExpType.None;}
        protected virtual ExpType typeBinaryExpression(SubTokenType Operation, ExpType OperandA, ExpType OperandB) { return ExpType.None; } */
        #endregion
    }
}
